﻿using DotNetCommon.Data;
using DotNetCommon.Extensions;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace DotNetCommon;

/// <summary>
/// 根据文件头读取识别图片的格式和尺寸信息, 示例:
/// <code>
/// var res = ImageDetector.Detect(@"d:\demo_256x256.jpg");
/// 
/// res.Format.ShouldeBe(EnumImageFormat.Jpeg);
/// res.Width.ShouldeBe(256);
/// res.Height.ShouldeBe(256);
/// </code>
/// </summary>
public class ImageDetector
{
    #region 实体定义
    public enum EnumImageFormat
    {
        Unkown = 0,
        Bmp = 1,
        Gif = 2,
        Ico = 3,
        Jpeg = 4,
        Png = 5,
        Wbmp = 6,
        Webp = 7,
        Tga = 8,
        Tiff = 9,
    }
    public struct ImageInfo
    {
        public EnumImageFormat Format { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
    }
    #endregion

    /// <inheritdoc cref="ImageDetector"/>
    public static Result<ImageInfo> Detect(string filePath)
    {
        using var fs = new FileInfo(filePath).OpenSequentialRead();
        return Detect(fs);
    }

    /// <summary>
    /// 根据文件头读取识别图片的格式和尺寸信息, 示例:
    /// <code>
    /// var fs = using File.OpenRead(@"d:\demo_256x256.jpg")
    /// var res = ImageDetector.Detect(fs);
    /// 
    /// res.Format.ShouldeBe(EnumImageFormat.Jpeg);
    /// res.Width.ShouldeBe(256);
    /// res.Height.ShouldeBe(256);
    /// </code>
    /// </summary>
    public static Result<ImageInfo> Detect(Stream stream)
    {
        try
        {
            var readers = DefaultImageReaders;
            var startPosition = stream.Position;
            foreach (var reader in readers)
            {
                try
                {
                    var res = reader(stream, startPosition);
                    if (res.Success) return res;
                }
                finally
                {
                    stream.Seek(startPosition, SeekOrigin.Begin);
                }
            }
            return Result.NotOk("读取失败");
        }
        catch (Exception ex)
        {
            return Result.NotOk($"ImageDetector.Detect 报错: {ex.Message}");
        }
    }

    #region 内部方法
    private static readonly List<Func<Stream, long, Result<ImageInfo>>> DefaultImageReaders = [
    ReadJpeg,
        ReadPng,
        ReadWebp,
        ReadGif,
        ReadBmp,
        ReadTiff,
        ReadIco,
        ReadTga,
    ];

    private static Result<ImageInfo> ReadPng(Stream stream, long startPosition)
    {
        var buffer = new byte[8];
        if (stream.Read(buffer, 0, 8) != 8) return Result.NotOk("读取错误");
        if (
           !(buffer[0] == 0x89 && buffer[1] == 0x50 &&
           buffer[2] == 0x4E && buffer[3] == 0x47 &&
           buffer[4] == 0x0D && buffer[5] == 0x0A &&
           buffer[6] == 0x1A && buffer[7] == 0x0A))
        {
            return Result.NotOk("非png");
        }
        stream.Seek(startPosition + 16, SeekOrigin.Begin);

        if (stream.Read(buffer, 0, 8) != 8) return Result.NotOk("读取错误");
        int width = BitConverter.ToInt32(buffer.Take(4).Reverse().ToArray(), 0);
        int height = BitConverter.ToInt32(buffer.Skip(4).Take(4).Reverse().ToArray(), 0);
        return Result.Ok(new ImageInfo
        {
            Format = EnumImageFormat.Png,
            Height = height,
            Width = width,
        });
    }

    private static Result<ImageInfo> ReadJpeg(Stream stream, long startPosition)
    {
        var buffer = new byte[8];
        if (stream.Read(buffer, 0, 2) != 2) return Result.NotOk("读取错误");
        if (!(buffer[0] == 0xFF && buffer[1] == 0xD8))
        {
            return Result.NotOk("非jpg");
        }
        while (stream.Read(buffer, 0, 1) == 1)
        {
            if (buffer[0] != 0xFF) continue;
            if (stream.Read(buffer, 0, 1) != 1) return Result.NotOk("读取错误");
            var markerType = buffer[0];
            if (markerType == 0xD9) // EOI标记，结束
                break;
            if (stream.Read(buffer, 0, 2) != 2) return Result.NotOk("读取错误");
            int length = (buffer[0] << 8) + buffer[1];

            if (markerType == 0xC0 || markerType == 0xC2)
            {
                //跳过第一个字节
                if (stream.Read(buffer, 0, 5) != 5) return Result.NotOk("读取错误");
                int height = (buffer[1] << 8) + buffer[2];
                int width = (buffer[3] << 8) + buffer[4];
                return Result.Ok(new ImageInfo
                {
                    Format = EnumImageFormat.Jpeg,
                    Height = height,
                    Width = width,
                });
            }
            if (length < 2 || stream.Position + length - 2 > stream.Length) return Result.NotOk("读取错误");
            stream.Seek(length - 2, SeekOrigin.Current);
        }
        return Result.NotOk("读取失败");
    }

    private static Result<ImageInfo> ReadBmp(Stream stream, long startPosition)
    {
        var buffer = new byte[8];
        if (stream.Read(buffer, 0, 2) != 2) return Result.NotOk("读取错误");
        if (!(buffer[0] == 0x42 && buffer[1] == 0x4D))
        {
            return Result.NotOk("非bmp");
        }
        stream.Seek(startPosition + 18, SeekOrigin.Begin);
        if (stream.Read(buffer, 0, 8) != 8) return Result.NotOk("读取错误");
        int width = BitConverter.ToInt32(buffer.Take(4).ToArray(), 0);
        int height = BitConverter.ToInt32(buffer.Skip(4).Take(4).ToArray(), 0);
        return Result.Ok(new ImageInfo
        {
            Format = EnumImageFormat.Bmp,
            Height = height,
            Width = width,
        });
    }

    private static Result<ImageInfo> ReadWebp(Stream stream, long startPosition)
    {
        byte[] buffer = new byte[30]; // 足够大以读取文件头和VP8/VP8L/VP8X块
        if (stream.Read(buffer, 0, 30) != 30) return Result.NotOk("读取错误");

        // 检查文件头是否为RIFF
        if (Encoding.ASCII.GetString(buffer, 0, 4) != "RIFF") return Result.NotOk("非webp");

        // 检查文件格式是否为WEBP
        if (Encoding.ASCII.GetString(buffer, 8, 4) != "WEBP") return Result.NotOk("非webp");

        // 检查是否为VP8/VP8L/VP8X块
        string chunkHeader = Encoding.ASCII.GetString(buffer, 12, 4);

        if (chunkHeader == "VP8 ")
        {
            // 跳过Start Code
            ushort width = BitConverter.ToUInt16(buffer, 26);
            ushort height = BitConverter.ToUInt16(buffer, 28);
            return Result.Ok(new ImageInfo
            {
                Format = EnumImageFormat.Webp,
                Width = width & 0x3FFF,
                Height = height & 0x3FFF,
            });
        }
        else if (chunkHeader == "VP8L")
        {
            uint dimensions = BitConverter.ToUInt32(buffer, 21);
            int width = (int)(dimensions & 0x3FFF) + 1;
            int height = (int)((dimensions >> 14) & 0x3FFF) + 1;

            return Result.Ok(new ImageInfo
            {
                Format = EnumImageFormat.Webp,
                Width = width,
                Height = height,
            });
        }
        else if (chunkHeader == "VP8X")
        {
            uint width = ReadUInt24(buffer, 24) + 1;
            uint height = ReadUInt24(buffer, 27) + 1;
            return Result.Ok(new ImageInfo
            {
                Format = EnumImageFormat.Webp,
                Width = (int)width,
                Height = (int)height,
            });
        }
        return Result.NotOk("读取失败");
    }

    private static uint ReadUInt24(byte[] buffer, int startIndex)
    {
        return (uint)(buffer[startIndex] | (buffer[startIndex + 1] << 8) | (buffer[startIndex + 2] << 16));
    }

    private static Result<ImageInfo> ReadGif(Stream stream, long startPosition)
    {
        byte[] buffer = new byte[10];
        if (stream.Read(buffer, 0, 10) != 10) return Result.NotOk("读取错误");

        if (!(
            buffer[0] == 0x47
            && buffer[1] == 0x49
            && buffer[2] == 0x46
            && buffer[3] == 0x38
            && (buffer[4] == 0x37 || buffer[4] == 0x39)
            && buffer[5] == 0x61))
        {
            return Result.NotOk("非gif");
        }

        int width = BitConverter.ToInt16(buffer, 6);
        int height = BitConverter.ToInt16(buffer, 8);
        return Result.Ok(new ImageInfo
        {
            Format = EnumImageFormat.Gif,
            Height = height,
            Width = width,
        });
    }

    private static Result<ImageInfo> ReadIco(Stream stream, long startPosition)
    {
        byte[] buffer = new byte[8];
        if (stream.Read(buffer, 0, 8) != 8) return Result.NotOk("读取错误");

        if (!(
            buffer[0] == 0x00
            && buffer[1] == 0x00
            && buffer[2] == 0x01
            && buffer[3] == 0x00
            ))
        {
            return Result.NotOk("非ico");
        }

        byte width = buffer[6];
        byte height = buffer[7];
        return Result.Ok(new ImageInfo
        {
            Format = EnumImageFormat.Gif,
            Height = height == 0 ? 256 : height,
            Width = width == 0 ? 256 : width,
        });
    }

    private static Result<ImageInfo> ReadTga(Stream stream, long startPosition)
    {
        byte[] buffer = new byte[18];
        if (stream.Read(buffer, 0, 18) != 18) return Result.NotOk("读取错误");

        if (!(
            (buffer[1] == 0 || buffer[1] == 1) &&   // 颜色映射类型：0或1
           (buffer[2] >= 1 && buffer[2] <= 3 ||    // 图像类型：1, 2, 3
            buffer[2] >= 9 && buffer[2] <= 11)   // 压缩图像类型：9, 10, 11
            ))
        {
            return Result.NotOk("非tga");
        }

        int width = buffer[12] + (buffer[13] << 8);
        int height = buffer[14] + (buffer[15] << 8);
        return Result.Ok(new ImageInfo
        {
            Format = EnumImageFormat.Tga,
            Height = height,
            Width = width,
        });

    }

    private static Result<ImageInfo> ReadTiff(Stream stream, long startPosition)
    {
        byte[] buffer = new byte[8];
        if (stream.Read(buffer, 0, 8) != 8) return Result.NotOk("读取错误");

        if (!((buffer[0] == 'I' && buffer[1] == 'I' && BitConverter.ToUInt16(buffer, 2) == 42) ||
        (buffer[0] == 'M' && buffer[1] == 'M' && BitConverter.ToUInt16(buffer.Reverse().ToArray(), 2) == 42)))
        {
            return Result.NotOk("非tiff");
        }

        // 读取字节顺序
        bool isLittleEndian = buffer[0] == 'I' && buffer[1] == 'I';

        Func<Stream, ushort> readUShort;
        Func<Stream, uint> readUInt;
        if (isLittleEndian)
        {
            readUShort = r =>
            {
                var bs = new byte[2];
                r.Read(bs);
                return BitConverter.ToUInt16(bs, 0);
            };
            readUInt = r =>
            {
                var bs = new byte[4];
                r.Read(bs);
                return BitConverter.ToUInt32(bs, 0);
            };
        }
        else
        {
            readUShort = r =>
            {
                var bs = new byte[2];
                r.Read(bs);
                return BitConverter.ToUInt16(bs.Reverse().ToArray(), 0);
            };
            readUInt = r =>
            {
                var bs = new byte[4];
                r.Read(bs);
                return BitConverter.ToUInt32(bs.Reverse().ToArray(), 0);
            };
        }

        // 跳转到第一个IFD偏移位置
        stream.Seek(startPosition + 4, SeekOrigin.Begin);
        uint ifdOffset = readUInt(stream);

        // 跳转到IFD并读取条目数量
        stream.Seek(startPosition + ifdOffset, SeekOrigin.Begin);
        ushort entryCount = readUShort(stream);

        // 查找宽度和高度条目
        uint width = 0, height = 0;
        for (int i = 0; i < entryCount; i++)
        {
            ushort tag = readUShort(stream);
            ushort type = readUShort(stream);
            readUInt(stream);
            uint valueOffset = readUInt(stream);

            if (tag == 256) // 图像宽度
            {
                width = type == 3 ? valueOffset & 0xFFFF : valueOffset;
            }
            else if (tag == 257) // 图像高度
            {
                height = type == 3 ? valueOffset & 0xFFFF : valueOffset;
            }
            if (width > 0 && height > 0)
            {
                break; // 找到宽度和高度
            }
        }
        if (width > 0 && height > 0)
        {
            return Result.Ok(new ImageInfo
            {
                Format = EnumImageFormat.Tiff,
                Height = (int)height,
                Width = (int)width,
            });
        }
        else
        {
            return Result.NotOk("读取失败");
        }
    }
    #endregion
}
